package com.kupdater.transform;

import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.sun.org.apache.bcel.internal.generic.ClassGen;
import com.kupdater.Constants;
import com.kupdater.util.TransformUtils;

/**
 * Created by IntelliJ IDEA.
 * User: Jan Ove
 * Date: 29.jan.2009
 * Time: 20:12:48
 * To change this template use File | Settings | File Templates.
 */


public abstract class Transformer {
	protected static Map<String, ClassGen> classMap = new HashMap<String, ClassGen>();

	private static List<Transformer> transformers = new LinkedList<Transformer>();

	private boolean hasRun = false;

	static {
		File directory = new File(Constants.DIRECTORY_TRANSFORM);
		for(File file : directory.listFiles()) {
			if(!file.getName().equals("Transformer.class") && !file.getName().equals(".svn")) {
				String name = file.getName().replaceAll(".class", "");
				try {
					Transformer transformer = (Transformer) Class.forName(Constants.PACKAGE_TRANSFORM + "." + name).newInstance();
					if(Transformer.class.isAssignableFrom(transformer.getClass())) {
						transformers.add(transformer);
					}
				} catch(InstantiationException e) {
					e.printStackTrace();
				} catch(IllegalAccessException e) {
					e.printStackTrace();
				} catch(ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public abstract Class<?>[] getDependencies();

	public abstract boolean isApplicable(ClassGen cG);

	public abstract boolean transform(ClassGen cG);

	public static void run(ClassGen[] classes) {
		List<Transformer> trans = organize(transformers);

		int count = trans.size();
		long start = System.currentTimeMillis();
		for(int i = 0; i < trans.size(); i++) {
			Transformer transformer = trans.get(i);
			for(ClassGen cG : classes) {
				if(transformer.isApplicable(cG)) {
					if(!transformer.transform(cG)) {
						System.err.println("\t[!!] Error in transform \"" + transformer.getClass().getSimpleName() + "\"");
					}
				}
			}
			transformers.remove(transformer);
		}
		long time = System.currentTimeMillis() - start;

        // Fucking restocking!!!!
        List<String> names = new LinkedList<String>();
		for(String[] out: TransformUtils.output){
            if(out == null || out.length != 2){
                System.out.println("woot?");
                break;
            }
            if(!names.contains(out[0])){
                names.add(out[0]);
            }
        }
        List<String[]> after = new LinkedList<String[]>();
        for(String name: names){
            for(String[] out: TransformUtils.output){
                if(out == null || out.length != 2){
                    System.out.println("woot?");
                    break;
                }
                if(out[0].equals(name)){
                    after.add(out);
                }
            }
        }

        String lastTransformer = "";
        for(String[] out: after){
            if(out == null || out.length != 2){
                System.out.println("woot?");
                break;
            }
            if(!out[0].equals(lastTransformer)){
                System.out.println("[ "+out[0].replaceAll("Transformer", "")+" ]");
                lastTransformer = out[0];
            }
            System.out.println(out[1]);
        }

		System.out.println("Completed " + count + " transform(s) in " + time + "ms.");

        System.out.println("\n");
        System.out.println("#######################");
        System.out.println("# Defined "+TransformUtils.classesDefined+"/"+TransformUtils.classesShouldDefine+" classes.");
        System.out.println("# Hooked "+TransformUtils.fieldsHooked+"/"+TransformUtils.fieldsShouldHook+" fields.");
        System.out.println("#######################");
	}

	private static List<Transformer> organize(List<Transformer> before) {
		List<Transformer> after = new LinkedList<Transformer>();
		while(!before.isEmpty()) {
			for(int i = 0; i < before.size(); i++) {
				Transformer transformer = before.get(i);
				if(!after.contains(transformer)) {
					if(canRun(transformer)) {
						after.add(transformer);
						before.remove(transformer);
					}
				}
			}
		}
		return after;
	}

	private static boolean canRun(Transformer transformer) {
		Class<?>[] dependencies = transformer.getDependencies();
		for(Class<?> dependency : dependencies) {
			for(Transformer t : transformers) {
				if(t.getClass().equals(dependency)) {
					if(!t.hasRun) {
						return false;
					}
				}
			}
		}
		return true;
	}
}
